<template>
  <div class="table-wrapper">
    <el-tooltip  placement="bottom" effect="dark" content="自定义设置">
      <Icon v-if="tableCustom" @click="openTableCustomView" class="setting"  icon="icon-park-outline:config" :size="20"/>
    </el-tooltip>
    <!-- vue-draggable-plus 拖拽组件 target：可拖拽元素list的父级 handle：拖拽元素 -->
    <VueDraggable
      v-if="isDrag"
      :model-value="dragDataList"
      target="tbody"
      handle=".drag-handle"
      v-bind="drag"
      :animation="150"
      @update="onDragUpdate"
    >
      <el-table
          ref="ElTableRef"
          v-bind="attrs"
          :data="dataList"
          style="width: 100%"
          :border="(attrs.border as boolean) ?? config?.table?.border"
          :stripe="(attrs.stripe as boolean) ?? config?.table?.stripe"
          :show-overflow-tooltip="(attrs.showOverflowTooltip as boolean) ?? config?.table?.showOverflowTooltip"
          :highlight-current-row="(attrs.highlightCurrentRow as boolean) ?? config?.table?.highlightCurrentRow"
      >
        <template #default>
          <TableColumn v-for="(item, index) in handleColumns" :item=item>
              <!-- TableColumn插槽透传 -->
              <template v-for="itemSlot in getTableColumnSlots(item)" :key="itemSlot" v-slot:[itemSlot]="temp">
                <slot :name="itemSlot" v-bind="temp"></slot>
              </template>
          </TableColumn>
        </template>
        <template #empty>
          <div class="emptyData">
            <div>暂无数据</div>
          </div>
        </template>
      </el-table>
    </VueDraggable>
    <div v-else>
      <el-table
          ref="ElTableRef"
          v-bind="attrs"
          :data="dataList"
          style="width: 100%"
          :border="(attrs.border as boolean) ?? config?.table?.border"
          :stripe="(attrs.stripe as boolean) ?? config?.table?.stripe"
          :show-overflow-tooltip="(attrs.showOverflowTooltip as boolean) ?? config?.table?.showOverflowTooltip"
          :highlight-current-row="(attrs.highlightCurrentRow as boolean) ?? config?.table?.highlightCurrentRow"
      >
        <template #default>
          <TableColumn v-for="(item, index) in handleColumns" :item=item>
              <!-- TableColumn插槽透传 -->
              <template v-for="itemSlot in getTableColumnSlots(item)" :key="itemSlot" v-slot:[itemSlot]="temp">
                <slot :name="itemSlot" v-bind="temp"></slot>
              </template>
          </TableColumn>
          <slot name=default></slot>
        </template>
        <template #empty>
          <div class="emptyData">
            <div>暂无数据</div>
          </div>
        </template>
      </el-table>
    </div>

    <!-- Table列字段自定义设置组件 -->
    <TableCustomView
      v-if="tableCustom"
      ref="TableCustomViewRef"
      :sourceColumns="columns"
      @update:custom-columns="onUpdateCustomColumns"
    >
      <!-- TableColumn表头插槽透传 -->
      <template v-for="itemSlot in getTableColumnHeaderSlot(handleColumns)" :key="itemSlot" v-slot:[itemSlot]="temp">
        <slot :name="itemSlot" v-bind="temp"></slot>
      </template>
    </TableCustomView>
  </div>
</template>

<script setup lang="ts">
  import '../style/index.scss'
  import { h, ref, computed, watch, useAttrs, nextTick, onMounted } from "vue";
  import { ElMessage, ElTable, ElImage, ElTag, ElTooltip } from "element-plus";
  import { componentConfig as config } from '@/components'
  import { VueDraggableProps, ElTableProps, TableColumnItem, ElTableColumnTypes, getFieldValue } from '@/components'
  import TableColumn from './TableColumn.vue'
  import { TableCustomView } from '@/components'
  import { getTableColumnSlots, getTableColumnHeaderSlot, recursionTableColumnList, useTableDrag } from '@/components'
  import { ElTableFns, getElTableFns } from '../../utils/el-table-fn'
  import { VueDraggable } from 'vue-draggable-plus'
  import { Icon } from '@/components';
  import DragBtn from './DragBtn.vue'
  import dayjs from 'dayjs'
  const attrs = useAttrs()
  const props = withDefaults(
    defineProps<{
      // 列表字段
      columns?: TableColumnItem[]
      // 是否开启列表字段设置工具
      tableCustom?: boolean
      // 列表数据
      data: any[]
      // vue-draggable-plus 组件参数与配置
      drag: VueDraggableProps
    } & /* @vue-ignore */ ElTableProps>(),
    {
      columns: () => [],
      tableCustom: true,
      data: () => [],
      drag: () => null
    }
  )
  const emit = defineEmits(["update:data"]);

  const ElTableRef = ref<InstanceType<typeof ElTable>>()
  const TableCustomViewRef = ref<InstanceType<typeof TableCustomView>>()


  // 处理后的Table列字段
  const handleColumns = ref<TableColumnItem[]>()
  // 是否需要拖拽
  const isDrag = computed(() => handleColumns.value.find(e=>e.type==='drag') ? true : false)


  /**
   * 获取prop对应字段值 并继续数据处理
   * @param row 行数据
   * @param fieldSource
   */
  const getValue = (row: any, item: TableColumnItem): { oldValue:any, value:any } => {
    const oldValue = getFieldValue(row, item.prop)
    let value = oldValue
    if (oldValue) {
      // 枚举值转换
      if (item.list) {
        value = item.list.find(e=>e.value === oldValue)?.label
      }
      // 日期格式化
      else if (item.format) {
        value = dayjs(oldValue).format(item.format)
      }
    }
    value = value ?? oldValue
    return { oldValue, value }
  }


  // TableColumnmr默认处理器
  const defaultHandle = (item: TableColumnItem) => {
    if(!item.render && !item.slot && item.prop) {
      item.render = (row) => {
        const { oldValue, value } =getValue(row, item)
        const { isHandle, props } = commonHandle(row, { ...item })
        if (item.tagType) {
          // el-tag标签组件
          let tagType = item.tagType[oldValue] ?? item.tagType?.defalut
          if (tagType) {
            return h(ElTag, { ...props, type: tagType }, value)
          }
        } else {
          return isHandle ? h('div', { ...props }, value) : value
        }
      }
    }
  }

  // 公共处理
  const commonHandle = (row: any, item: TableColumnItem) => {
    const props = { ...item }
    props.style = props.style ?? {}
    let { style, clickProps } = props
    let isHandle = false

    // 点击
    if (clickProps) {
      const { clickable, onclick, style: clickStyle  } = clickProps
      if (onclick && (!clickable || clickable(row))) {
        props.onclick = (event) => onclick(row, event)
        style.cursor = 'pointer'
        style.color = '#409eff'
        style = { ...style, clickStyle }
      }
    }
    isHandle = isHandle || Object.keys(style).length > 0
    return {
        isHandle,
        props: {
          ...props,
          style
        }
      }
  }

  // TableColumn类型处理器
  const typeHandles = {
    // 拖拽组件
    drag: (item: TableColumnItem) => {
      item.render = () => h(DragBtn, null, null)
      item.renderHeader = () => h(DragBtn, null, null)
    },
    // el-image图片预览组件
    image: (item: TableColumnItem) => {
      // preview-teleported 是否插入至 body 元素上, 防止图片预览遮挡
      item.previewTeleported = true
      item.render = (row) => {
        const src = getFieldValue(row, item.prop)
        let previewSrcList = getFieldValue(row, item.srcList) ?? [src]
        previewSrcList = Array.isArray(previewSrcList) ? previewSrcList : [previewSrcList]
        return h(ElImage, { ...item, src, previewSrcList }, null)
      }
    },
  }

  // 处理TableColumn
  const handleTableColumn = (list: TableColumnItem[]) => {
    // 递归TableColumn处理
    recursionTableColumnList(list, (item: TableColumnItem) => {
      // element plus 原生类型不处理
      if(!ElTableColumnTypes.includes(item.type)) {
        // 根据类型进行特殊处理 无对应类型使用默认处理器
        item.type && typeHandles[item.type] ? typeHandles[item.type](item) : defaultHandle(item)
      }
    })
    return list
  }

  handleColumns.value = handleTableColumn(props.columns)

  // 自定义Table列字段设置更新事件
  const onUpdateCustomColumns = (newColumns: TableColumnItem[]) =>{
    handleColumns.value = handleTableColumn(newColumns)
    nextTick(()=>{
      ElTableRef.value.doLayout()
    })
  }



  // 列表数据
  const dataList = ref<any[]>(props.data)
  watch(
    () => props.data,
    () => {
      dataList.value = props.data;
    }
  )

  // vue-draggable-plus 和 element plus table 共用一个dom视图
  // element plus table 会字段将tree结构数据扁平化展示（父级和children在同一层）
  // vue-draggable-plus 绑定的数据要扁平化映射， 防止视图数组的下标无法和绑定的数据进行关联
  const tableDragHandler = useTableDrag(dataList)
  const dragDataList = computed<any[]>(() => {
    return tableDragHandler.getFlatList()
  })

  // vue-draggable-plus 元素位置更新事件
  const onDragUpdate = (event) => {
    try {
      tableDragHandler.swapItem(dragDataList.value[event.oldIndex], dragDataList.value[event.newIndex])
    } catch (e) {
      ElMessage.warning(e.message)
      return
    }
    emit("update:data", dataList.value)
    props?.drag?.onUpdateList && props?.drag?.onUpdateList(dataList.value, event)
    props?.drag?.onUpdate && props?.drag?.onUpdate(event)
  }


  // 打开Table列字段自定义设置组件
  const openTableCustomView = () => {
    TableCustomViewRef.value.openDialog()
  }

  const expose: ElTableFns = {
    clearSelection: () => null,
    getSelectionRows: () => null,
    toggleRowSelection: () => null,
    toggleAllSelection: () => null,
    toggleRowExpansion: () => null,
    setCurrentRow:  () => null,
  }

  onMounted(() => {
    Object.assign(expose, getElTableFns(ElTableRef.value))
  })

  defineExpose(expose)

</script>


<style scoped>
</style>
